home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung 2 / Power-Programmierung CD 2 (Tewi)(1994).iso / gnu / gnulib / ohlutil / rm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-08-24  |  15.9 KB  |  657 lines

  1. /* `rm' file deletion utility for GNU.
  2.    Copyright (C) 1988, 1989, 1990 Free Software Foundation, Inc.
  3.    ChangeLog: changelog
  4.  
  5.    This program is free software; you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation; either version 1, or (at your option)
  8.    any later version.
  9.  
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  18.  
  19. /* Written by Paul Rubin, David MacKenzie, and Richard Stallman. */
  20.  
  21. /*
  22.  * MS-DOS port (c) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet
  23.  *
  24.  * To this port, the same copying conditions apply as to the
  25.  * original release.
  26.  *
  27.  * IMPORTANT:
  28.  * This file is not identical to the original GNU release!
  29.  * You should have received this code as patch to the official
  30.  * GNU release.
  31.  *
  32.  * MORE IMPORTANT:
  33.  * This port comes with ABSOLUTELY NO WARRANTY.
  34.  *
  35.  * $Header: e:/gnu/fileutil/RCS/rm.c'v 1.3.0.3 90/06/29 14:40:38 tho Exp $
  36.  */
  37.  
  38. #include <stdio.h>
  39. #include <sys/types.h>
  40. #include <errno.h>
  41. #include "system.h"
  42. #include "getopt.h"
  43.  
  44. #ifdef MSDOS
  45.  
  46. #include <string.h>
  47. #include <malloc.h>
  48. #include <io.h>
  49. #include <direct.h>
  50.  
  51. #define unlink(name)        force_unlink (name)
  52. static  int force_unlink (char *filename);
  53.  
  54. extern  void main (int argc, char **argv);
  55. static  int rm (void);
  56. static  int remove_file (struct stat *statp);
  57. static  int remove_dir (struct stat *statp);
  58. static  int clear_directory (struct stat *statp);
  59. static  int yesno (void);
  60. static  char *basename (char *name);
  61. static  char *stpcpy (char *dest, char *source);
  62. static  void usage (void);
  63. static  char *xmalloc (unsigned n);
  64. static  char *xrealloc (char *p, unsigned n);
  65. static  void strip_trailing_slashes (char *path);
  66.  
  67. extern  void error (int status, int errnum, char *message, ...);
  68. extern  int eaccess (char *path, int mode);
  69. extern  int eaccess_stat (struct stat *statp, int mode);
  70.  
  71. #endif /* MSDOS */
  72.  
  73. #ifdef STDC_HEADERS
  74. #include <stdlib.h>
  75. #include <errno.h>
  76. #else
  77. char *malloc ();
  78. char *realloc ();
  79.  
  80. extern int errno;
  81. #endif
  82.  
  83. char *basename ();
  84. char *stpcpy ();
  85. char *xmalloc ();
  86. char *xrealloc ();
  87. int check_stack ();
  88. int clear_directory ();
  89. int remove_dir ();
  90. int remove_file ();
  91. int rm ();
  92. int yesno ();
  93. void error ();
  94. void strip_trailing_slashes ();
  95. void usage ();
  96.  
  97. /* Path of file now being processed; extended as necessary. */
  98. char *pathname;
  99.  
  100. /* Amount of space currently allocated in `pathname';
  101.    made larger when necessary, but never smaller.  */
  102. int pnsize;
  103.  
  104. /* Name under which this program was run.  */
  105. char *program_name;
  106.  
  107. /* If nonzero, display the name of each file removed. */
  108. int verbose;
  109.  
  110. /* If nonzero, ignore unremovable files (print no error messages and
  111.    always exit with a status of zero). */
  112. int ignore_errors;
  113.  
  114. /* If nonzero, recursively remove directories. */
  115. int recursive;
  116.  
  117. /* If nonzero, ask no questions. */
  118. int override_mode;
  119.  
  120. /* If nonzero, query the user about whether to remove each file. */
  121. int interactive;
  122.  
  123. /* If nonzero, stdin is not connected to a tty. */
  124. int stdin_not_tty;
  125.  
  126. struct option long_opts[] =
  127. {
  128.   {"force", 0, NULL, 'f'},
  129.   {"interactive", 0, &interactive, 1},
  130.   {"override", 0, &override_mode, 1},
  131.   {"recursive", 0, &recursive, 1},
  132.   {"verbose", 0, &verbose, 1},
  133.   {NULL, 0, NULL, 0}
  134. };
  135.  
  136. void
  137. main (argc, argv)
  138.      int argc;
  139.      char **argv;
  140. {
  141.   int err = 0;
  142.   int c;
  143.   int ind;
  144.  
  145.   verbose = ignore_errors = recursive = interactive = override_mode = 0;
  146.   pnsize = 256;
  147.   pathname = xmalloc (pnsize);
  148.   program_name = argv[0];
  149.  
  150. #ifdef MSDOS
  151.   strlwr (program_name);
  152. #endif /* MSDOS */
  153.  
  154.   while ((c = getopt_long (argc, argv, "fiorvR", long_opts, &ind)) != EOF)
  155.     {
  156.       if (c == 0 && long_opts[ind].flag == 0)
  157.     c = long_opts[ind].val;
  158.       switch (c)
  159.     {
  160.     case 0:            /* Long option. */
  161.       break;
  162.     case 'f':
  163.       ignore_errors = 1;
  164.       override_mode = 1;
  165.       break;
  166.     case 'i':
  167.       interactive = 1;;
  168.       break;
  169.     case 'o':
  170.       override_mode = 1;
  171.       break;
  172.     case 'r':
  173.     case 'R':
  174.       recursive = 1;
  175.       break;
  176.     case 'v':
  177.       verbose = 1;
  178.       break;
  179.     default:
  180.       usage ();
  181.     }
  182.     }
  183.  
  184.   if (optind == argc)
  185.     usage ();
  186.  
  187.   if (interactive)
  188.     {
  189.       override_mode = 0;
  190.       ignore_errors = 0;
  191.     }
  192.   stdin_not_tty = !isatty (0);
  193.  
  194.   for (; optind < argc; optind++)
  195.     {
  196.       int len;
  197.  
  198.       strip_trailing_slashes (argv[optind]);
  199.       if (!strcmp (basename (argv[optind]), ".."))
  200.     {
  201.       if (!ignore_errors)
  202.         error (0, 0, "removal of `%s' is not allowed", argv[optind]);
  203.       err++;
  204.       continue;
  205.     }
  206.  
  207.       len = strlen (argv[optind]);
  208.       if (len + 1 > pnsize)
  209.     {
  210.       free (pathname);
  211.       pnsize = 2 * (len + 1);
  212.       pathname = xmalloc (pnsize);
  213.     }
  214.       strcpy (pathname, argv[optind]);
  215. #ifdef MSDOS
  216.       strlwr (pathname);
  217. #endif /* !MSDOS */
  218.       err += rm ();
  219.     }
  220.  
  221.   exit (err > 0 && !ignore_errors);
  222. }
  223.  
  224. /* Remove file or directory `pathname' after checking appropriate things.
  225.    Return 0 if `pathname' is removed, 1 if not. */
  226.  
  227. int
  228. rm ()
  229. {
  230.   struct stat sbuf;
  231.  
  232.   if (lstat (pathname, &sbuf) < 0)
  233.     {
  234.       if (!ignore_errors)
  235.     error (0, errno, "%s", pathname);
  236.       return 1;
  237.     }
  238.  
  239.   if (verbose)
  240.     printf ("  %s\n", pathname);
  241.  
  242.   if ((sbuf.st_mode & S_IFMT) == S_IFDIR)
  243.     return remove_dir (&sbuf);
  244.   else
  245.     return remove_file (&sbuf);
  246. }
  247.  
  248. /* Query the user if appropriate, and if ok try to remove the
  249.    non-directory `pathname', which `statp' contains info about.
  250.    Return 0 if `pathname' is removed, 1 if not. */
  251.  
  252. int
  253. remove_file (statp)
  254.      struct stat *statp;
  255. {
  256.   if (interactive)
  257.     {
  258.       fprintf (stderr, "%s: remove `%s'? ", program_name, pathname);
  259.       if (!yesno ())
  260.     return 1;
  261.     }
  262.   else if (!override_mode)
  263.     {
  264.       int may_overwrite;
  265.  
  266.       /* Treat the file as nonwritable if it lacks write permission bits,
  267.      even if we are root.  */
  268. #ifdef S_IFLNK
  269.       if ((statp->st_mode & S_IFMT) == S_IFLNK)
  270.     may_overwrite = 1;
  271.       else
  272. #endif
  273.     may_overwrite = eaccess_stat (statp, W_OK) == 0
  274.       && (statp->st_mode & 0222);
  275.       
  276.       if (!may_overwrite)
  277.     {
  278.       if (stdin_not_tty)
  279.         {
  280.           if (!ignore_errors)
  281.         error (0, 0, "%s: no write permission", pathname);
  282.           return 1;
  283.         }
  284.       fprintf (stderr, "%s: override mode %04o for `%s'? ",
  285.            program_name, statp->st_mode & 0777, pathname);
  286.       if (!yesno ())
  287.         return 1;
  288.     }
  289.     }
  290.  
  291.   if (unlink (pathname))
  292.     {
  293.       if (!ignore_errors)
  294.     error (0, errno, "%s", pathname);
  295.       return 1;
  296.     }
  297.   return 0;
  298. }
  299.  
  300. /* If not in recursive mode, print an error message and return 1.
  301.    Otherwise, query the user if appropriate, then try to recursively
  302.    remove directory `pathname', which `statp' contains info about.
  303.    Return 0 if `pathname' is removed, 1 if not. */
  304.  
  305. int
  306. remove_dir (statp)
  307.      struct stat *statp;
  308. {
  309.   int err;
  310.  
  311.   if (!recursive)
  312.     {
  313.       if (!ignore_errors)
  314.     error (0, 0, "`%s' is a directory", pathname);
  315.       return 1;
  316.     }
  317.  
  318. #ifdef MSDOS
  319.   if (eaccess_stat (statp, R_OK))
  320. #else /* not MSDOS */
  321.   if (eaccess_stat (statp, R_OK | X_OK))
  322. #endif /* not MSDOS */
  323.     {
  324.       /* POSIX.2 draft 9 says to only complain here if the dir is
  325.      not empty, but I can see no way to find out whether it is
  326.      empty without being able to read it. */
  327.       if (!ignore_errors)
  328.     error (0, errno, "%s", pathname);
  329.       return 1;
  330.     }
  331.  
  332.   if (interactive)
  333.     {
  334.       fprintf (stderr, "%s: recursively descend directory `%s'? ",
  335.            program_name, pathname);
  336.       if (!yesno ())
  337.     return 1;
  338.     }
  339.  
  340.   err = clear_directory (statp);
  341.   if (err == 0)
  342.     {
  343.       if (interactive)
  344.     {
  345.       fprintf (stderr, "%s: remove directory `%s'? ",
  346.            program_name, pathname);
  347.       if (!yesno ())
  348.         return 1;
  349.     }
  350.       err = rmdir (pathname) != 0;
  351.       if (err != 0 && !ignore_errors)
  352.     error (0, errno, "%s", pathname);
  353.     }
  354.   return err;
  355. }
  356.  
  357. /* An element in a stack of pointers into `pathname'.
  358.    `pathp' points to where in `pathname' the terminating '\0' goes
  359.    for this level's directory name. */
  360. struct pathstack
  361. {
  362.   struct pathstack *next;
  363.   char *pathp;
  364.   ino_t inum;
  365. };
  366.  
  367. /* Linked list of pathnames of directories in progress in recursive rm.
  368.    The entries actually contain pointers into `pathname'.
  369.    `pathstack' is the current deepest level. */
  370. static struct pathstack *pathstack = NULL;
  371.  
  372. /* Read directory `pathname' and remove all of its entries,
  373.    avoiding use of chdir.
  374.    On entry, `statp' points to the results of stat on `pathname'.
  375.    Return 0 for success, error count for failure.
  376.    Upon return, `pathname' will have the same contents as before,
  377.    but its address might be different; in that case, `pnsize' will
  378.    be larger, as well. */
  379.  
  380. int
  381. clear_directory (statp)
  382.      struct stat *statp;
  383. {
  384.   DIR *dirp;
  385.   struct direct *dp;
  386.   char *name_space;        /* Copy of directory's filenames. */
  387.   char *namep;            /* Current entry in `name_space'. */
  388.   unsigned name_size;        /* Bytes allocated for `name_space'. */
  389.   ino_t *inode_space;        /* Copy of directory's inodes. */
  390.   ino_t *inodep;        /* Current entry in `inode_space'. */
  391.   unsigned inode_size;        /* Bytes allocated for `inode_space'. */
  392.   int name_length;        /* Length of filename in `namep' plus '\0'. */
  393.   int pathname_length;        /* Length of `pathname'. */
  394.   int err = 0;            /* Return status. */
  395.   struct pathstack pathframe;    /* New top of stack. */
  396.   struct pathstack *pp;        /* Temporary. */
  397.  
  398.   errno = 0;
  399.   dirp = opendir (pathname);
  400.   if (dirp == NULL)
  401.     {
  402.       if (!ignore_errors)
  403.     error (0, errno, "%s", pathname);
  404.       return 1;
  405.     }
  406.  
  407. #ifdef MSDOS                /* stat () it ourselves ... */
  408.   statp->st_size = 0L;
  409.   for (dp = readdir (dirp); dp != NULL; dp = readdir (dirp))
  410.     statp->st_size += strlen(dp->d_name) + 1;
  411.   seekdir(dirp, 0L);
  412. #endif /* MSDOS */
  413.  
  414.   name_size = statp->st_size;
  415.   name_space = (char *) xmalloc (name_size);
  416.   namep = name_space;
  417.  
  418. #ifndef MSDOS
  419.   inode_size = statp->st_size;
  420.   inode_space = (ino_t *) xmalloc (inode_size);
  421.   inodep = inode_space;
  422. #endif /* not MSDOS */
  423.  
  424.   while ((dp = readdir (dirp)) != NULL)
  425.     {
  426.       /* Skip "." and ".." (some NFS filesystems' directories lack them). */
  427.       if (dp->d_name[0] != '.'
  428.       || (dp->d_name[1] != '\0'
  429.           && (dp->d_name[1] != '.' || dp->d_name[2] != '\0')))
  430.     {
  431.       unsigned size_needed = (namep - name_space) + NLENGTH (dp) + 2;
  432.  
  433.       if (size_needed > name_size)
  434.         {
  435.           char *new_name_space;
  436.  
  437.           while (size_needed > name_size)
  438.         name_size += 1024;
  439.  
  440.           new_name_space = xrealloc (name_space, name_size);
  441.           namep += new_name_space - name_space;
  442.           name_space = new_name_space;
  443.         }
  444.       namep = stpcpy (namep, dp->d_name) + 1;
  445.  
  446. #ifndef MSDOS
  447.       if (inodep == inode_space + inode_size)
  448.         {
  449.           ino_t *new_inode_space;
  450.  
  451.           inode_size += 1024;
  452.           new_inode_space = (ino_t *) xrealloc (inode_space, inode_size);
  453.           inodep += new_inode_space - inode_space;
  454.           inode_space = new_inode_space;
  455.         }
  456.       *inodep++ = dp->d_ino;
  457. #endif /* not MSDOS */
  458.     }
  459.     }
  460.   *namep = '\0';
  461.   closedir (dirp);
  462.   
  463.   pathname_length = strlen (pathname);
  464.  
  465.   for (namep = name_space, inodep = inode_space; *namep != '\0';
  466.        namep += name_length, inodep++)
  467.     {
  468.       name_length = strlen (namep) + 1;
  469.  
  470.       /* Satisfy GNU requirement that filenames can be arbitrarily long. */
  471.       if (pathname_length + 1 + name_length > pnsize)
  472.     {
  473.       char *new_pathname;
  474.  
  475.       pnsize = (pathname_length + 1 + name_length) * 2;
  476.       new_pathname = xrealloc (pathname, pnsize);
  477.       /* Update the all the pointers in the stack to use the new area. */
  478.       for (pp = pathstack; pp != NULL; pp = pp->next)
  479.         pp->pathp += new_pathname - pathname;
  480.       pathname = new_pathname;
  481.     }
  482.  
  483.       /* Add a new frame to the top of the path stack. */
  484.       pathframe.pathp = pathname + pathname_length;
  485.       pathframe.inum = *inodep;
  486.       pathframe.next = pathstack;
  487.       pathstack = &pathframe;
  488.  
  489.       /* Append '/' and the filename to current pathname, take care of the
  490.      file (which could result in recursive calls), and take the filename
  491.      back off. */
  492.  
  493.       *pathstack->pathp = '/';
  494.       strcpy (pathstack->pathp + 1, namep);
  495.  
  496. #ifdef MSDOS
  497.       if (rm ())
  498. #else /* not MSDOS */
  499.       /* If the i-number has already appeared, there's an error. */
  500.       if (check_stack (pathstack->next, pathstack->inum) || rm ())
  501. #endif /* not MSDOS */
  502.     err++;
  503.  
  504.       *pathstack->pathp = '\0';
  505.       pathstack = pathstack->next;    /* Pop the stack. */
  506.     }
  507.   free (name_space);
  508. #ifndef MSDOS
  509.   free (inode_space);
  510. #endif /* not MSDOS */
  511.   return err;
  512. }
  513.  
  514. #ifndef MSDOS
  515. /* If STACK does not already have an entry with the same i-number as INUM,
  516.    return 0. Otherwise, ask the user whether to continue;
  517.    if yes, return 1, and if no, exit.
  518.    This assumes that no one tries to remove filesystem mount points;
  519.    doing so could cause duplication of i-numbers that would not indicate
  520.    a corrupted file system. */
  521.  
  522. int
  523. check_stack (stack, inum)
  524.      struct pathstack *stack;
  525.      ino_t inum;
  526. {
  527.   struct pathstack *p;
  528.  
  529.   for (p = stack; p != NULL; p = p->next)
  530.     {
  531.       if (p->inum == inum)
  532.     {
  533.       fprintf (stderr, "\
  534. %s: WARNING: Circular directory structure.\n\
  535. This almost certainly means that you have a corrupted file system.\n\
  536. NOTIFY YOUR SYSTEM MANAGER.\n\
  537. Cycle detected:\n\
  538. %s\n\
  539. is the same file as\n", program_name, pathname);
  540.       *p->pathp = '\0';    /* Truncate pathname. */
  541.       fprintf (stderr, "%s\n", pathname);
  542.       *p->pathp = '/';    /* Put it back. */
  543.       if (stdin_not_tty)
  544.         exit (1);
  545.       fprintf (stderr, "%s: continue? ", program_name);
  546.       if (!yesno ())
  547.         exit (1);
  548.       return 1;
  549.     }
  550.     }
  551.   return 0;
  552. }
  553. #endif /* !MSDOS */
  554.  
  555. /* Query the user for a line from the keyboard;
  556.    return 1 if yes, 0 otherwise. */
  557.  
  558. int
  559. yesno ()
  560. {
  561.   int c, c2;
  562.  
  563.   fflush (stderr);
  564.   c = getchar ();
  565.   if (c == '\n')
  566.     return 0;
  567.   while ((c2 = getchar ()) != '\n' && c2 != EOF)
  568.     ;
  569.  
  570.   return c == 'y' || c == 'Y';
  571. }
  572.  
  573. /* Remove trailing slashes from PATH; they cause some system calls to fail. */
  574.  
  575. void
  576. strip_trailing_slashes (path)
  577.      char *path;
  578. {
  579.   int last;
  580.  
  581.   last = strlen (path) - 1;
  582.   while (last > 0 && path[last] == '/')
  583.     path[last--] = '\0';
  584. }
  585.  
  586. char *
  587. xmalloc (n)
  588.      unsigned n;
  589. {
  590.   char *p;
  591.  
  592.   p = malloc (n);
  593.   if (p == 0)
  594.     error (2, 0, "virtual memory exhausted");
  595.   return p;
  596. }
  597.  
  598. char *
  599. xrealloc (p, n)
  600.      char *p;
  601.      unsigned n;
  602. {
  603.   p = realloc (p, n);
  604.   if (p == 0)
  605.     error (2, 0, "virtual memory exhausted");
  606.   return p;
  607. }
  608.  
  609. /* Return `name' with any leading path stripped off.  */
  610.  
  611. char *
  612. basename (name)
  613.      char *name;
  614. {
  615.   char *base;
  616.  
  617.   base = rindex (name, '/');
  618.   return base ? base + 1 : name;
  619. }
  620.  
  621. /* Copy SOURCE into DEST, stopping after copying the first '\0', and
  622.    return a pointer to the '\0' at the end of DEST;
  623.    in other words, return DEST + strlen (SOURCE). */
  624.  
  625. char *
  626. stpcpy (dest, source)
  627.      char *dest;
  628.      char *source;
  629. {
  630.   while ((*dest++ = *source++) != 0)
  631.     /* Do nothing. */ ;
  632.   return dest - 1;
  633. }
  634.  
  635. void
  636. usage ()
  637. {
  638.   fprintf (stderr, "\
  639. Usage: %s [-fiorvR] [+force] [+interactive] [+override] [+recursive]\n\
  640.        [+verbose] path...\n",
  641.        program_name);
  642.   exit (1);
  643. }
  644.  
  645. #ifdef MSDOS
  646. int
  647. force_unlink(char *filename)
  648. {
  649.   if (access( filename, 2))            /* read only */
  650.     if (chmod( filename, S_IREAD|S_IWRITE))
  651.       error (0, errno, "can't force write permission for %s", filename);
  652.  
  653. #undef unlink                    /* nasty tricks ... */
  654.   return unlink (filename);
  655. }
  656. #endif /* MSDOS */
  657.